﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using ZOIL.DataModel;
using ZOIL.Framework.Core;

namespace ZOIL.Library.Behaviors
{
    public class PluginSelectionBehavior : PluginBehavior<ZLandscapeCanvas, SelectionBehavior>
    {
        #region private fields

        private AdornerLayer _adornerLayer;

        #endregion

        #region protected properties

        protected AdornerLayer AdornerLayer
        {
            get
            {
                if (_adornerLayer == null)
                {
                    var window = Window.GetWindow(AssociatedObject);

                    if (window != null)
                    {
                        var visual = window.Content as Visual;

                        if (visual != null)
                            _adornerLayer = AdornerLayer.GetAdornerLayer(visual);
                    }
                }
                return _adornerLayer;
            }
        }

        protected UIElement AssociatedObjectParent
        {
            get { return AssociatedObject.Parent as UIElement; }
        }

        #endregion

        #region overrides

        protected override void OnParentBehaviorChanged(SelectionBehavior oldParentBehavior, SelectionBehavior newParentBehavior)
        {
            CreateOneWayParentBehaviorBinding(SelectionBehavior.SelectionModeProperty, SelectionModeProperty);
            CreateOneWayParentBehaviorBinding(SelectionBehavior.TargetTypeProperty, TargetTypeProperty);
        }

        #endregion

        #region dependency properties

        #region SelectionMode

        /// <summary>
        /// SelectionMode Dependency Property
        /// </summary>
        public static readonly DependencyProperty SelectionModeProperty = SelectionBehavior.SelectionModeProperty.AddOwner(typeof(PluginSelectionBehavior), new FrameworkPropertyMetadata(SelectionModes.BoundingBox, OnSelectionModeChanged));

        /// <summary>
        /// Gets or sets the SelectionMode property. This dependency property 
        /// indicates ....
        /// </summary>
        public SelectionModes SelectionMode
        {
            get { return (SelectionModes)GetValue(SelectionModeProperty); }
            set { SetValue(SelectionModeProperty, value); }
        }

        /// <summary>
        /// Handles changes to the SelectionMode property.
        /// </summary>
        private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (PluginSelectionBehavior)d;
            var oldSelectionMode = (SelectionModes)e.OldValue;
            var newSelectionMode = target.SelectionMode;
            target.OnSelectionModeChanged(oldSelectionMode, newSelectionMode);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the SelectionMode property.
        /// </summary>
        protected virtual void OnSelectionModeChanged(SelectionModes oldSelectionMode, SelectionModes newSelectionMode)
        {
        }

        #endregion

        #region TargetType

        /// <summary>
        /// TargetType Dependency Property
        /// </summary>
        public static readonly DependencyProperty TargetTypeProperty = DependencyProperty.Register("TargetType", typeof(Type), typeof(PluginSelectionBehavior), new FrameworkPropertyMetadata(typeof(DependencyObject), OnTargetTypeChanged));

        /// <summary>
        /// Gets or sets the TargetType property. This dependency property 
        /// indicates ....
        /// </summary>
        public Type TargetType
        {
            get { return (Type)GetValue(TargetTypeProperty); }
            set { SetValue(TargetTypeProperty, value); }
        }

        /// <summary>
        /// Handles changes to the TargetType property.
        /// </summary>
        private static void OnTargetTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (PluginSelectionBehavior)d;
            var oldTargetType = (Type)e.OldValue;
            var newTargetType = target.TargetType;
            target.OnTargetTypeChanged(oldTargetType, newTargetType);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the TargetType property.
        /// </summary>
        protected virtual void OnTargetTypeChanged(Type oldTargetType, Type newTargetType)
        {
        }

        #endregion

        #endregion

        #region Selection Handling

        protected void StartSelection(Selection selection)
        {
            AdornerLayer.Add(new SelectionView(AssociatedObjectParent, selection, SelectionMode));
            ParentBehavior.RaiseSelectionStartedEvent(selection);
        }

        protected void ChangeSelection(Selection selection)
        {
            ParentBehavior.RaiseSelectionChangedEvent(selection);
        }

        protected void FinishSelection(Selection selection)
        {
            var selectionView = AdornerLayer.GetAdorners(AssociatedObjectParent).OfType<SelectionView>().Where(view => view.Selection == selection).Single();
            AdornerLayer.Remove(selectionView);

            if (TargetType != null)
                selection.SelectedElements = new List<DependencyObject>(ZHitTestHelper.GetElementsInGeometry(selection.SelectionGeometry, AssociatedObject, TargetType));
            else
                selection.SelectedElements = new List<DependencyObject>(ZHitTestHelper.GetElementsInGeometry(selection.SelectionGeometry, AssociatedObject));

            ParentBehavior.RaiseSelectionFinishedEvent(selection);
        }

        protected void CancelSelection(Selection selection)
        {
            ParentBehavior.RaiseSelectionCancelledEvent(selection);
        }

        #endregion
    }
}
